<?php

namespace CoffeeScript;

Init::init();

define('LEVEL_TOP', 1);
define('LEVEL_PAREN', 2);
define('LEVEL_LIST', 3);
define('LEVEL_COND', 4);
define('LEVEL_OP', 5);
define('LEVEL_ACCESS', 6);

define('TAB', '  ');

define('IDENTIFIER_STR', '[$A-Za-z_\x7f-\x{ffff}][$\w\x7f-\x{ffff}]*');
define('IDENTIFIER', '/^' . IDENTIFIER_STR . '$/u');
define('IS_STRING', '/^[\'"]/');
define('SIMPLENUM', '/^[+-]?\d+$/');
define('METHOD_DEF', '/^(?:(' . IDENTIFIER_STR . ')\.prototype(?:\.(' . IDENTIFIER_STR . ')|\[("(?:[^\\\\"\r\n]|\\\\.)*"|\'(?:[^\\\\\'\r\n]|\\\\.)*\')\]|\[(0x[\da-fA-F]+|\d*\.?\d+(?:[eE][+-]?\d+)?)\]))|(' . IDENTIFIER_STR . ')$/u');

class Nodes
{

    static function multident($code, $tab)
    {
        $code = preg_replace('/\n/', "\n{$tab}", $code);
        return preg_replace('/\s+$/', '', $code);
    }

    static function unfold_soak($options, $parent, $name)
    {
        if (!(isset($parent->{$name}) && $parent->{$name} && $ifn = $parent->{$name}->unfold_soak($options))) {
            return NULL;
        }

        $parent->{$name} = $ifn->body;
        $ifn->body = yy('Value', $parent);

        return $ifn;
    }

    static function utility($name)
    {
        Scope::$root->assign($ref = "__$name", call_user_func(array(__CLASS__, "utility_$name")));

        return $ref;
    }

    static function utility_bind()
    {
        return 'function(fn, me){ return function(){ return fn.apply(me, arguments); }; }';
    }

    static function utility_extends()
    {
        return 'function(child, parent) { '
                . 'for (var key in parent) { '
                . 'if (' . self::utility('hasProp') . '.call(parent, key)) '
                . 'child[key] = parent[key]; '
                . '} '
                . 'function ctor() { '
                . 'this.constructor = child; '
                . '} '
                . 'ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; '
                . 'return child; '
                . '}';
    }

    static function utility_hasProp()
    {
        return '{}.hasOwnProperty';
    }

    static function utility_indexOf()
    {
        return '[].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }';
    }

    static function utility_slice()
    {
        return '[].slice';
    }

    /**
     * Since PHP can't return values from __construct, and some of the node
     * classes rely heavily on this feature in JavaScript, we use this function
     * instead of 'new'.
     */
    static function yy($type)
    {
        $args = func_get_args();
        array_shift($args);

        $type = __NAMESPACE__ . '\yy_' . $type;

        $inst = new $type;
        $inst = call_user_func_array(array($inst, 'constructor'), $args);

        return $inst;
    }

}

?>
